layui.define(['jquery', 'form', 'upload', 'sortable'], exports => {
    let $ = layui.jquery, form = layui.form, upload = layui.upload, sortable = layui.sortable, formbuilder = {};

    /*组件库模板json*/
    let templateJsonArr = [{
        'active': false,
        'type': 'input',
        'label': '输入框',
        'name': 'input',
        'value': '',
        'placeholder': '文本、数值、手机、邮箱',
        'subtype': '',
        'required': false
    }, {
        'active': false,
        'type': 'select',
        'label': '下拉框',
        'name': 'select',
        'value': '',
        'placeholder': '请选择',
        'required': false,
        'data': [{
            'key': 'option1',
            'value': '选项一',
            'selected': false
        }, {
            'key': 'option2',
            'value': '选项二',
            'selected': false
        }]
    }, {
        'active': false,
        'type': 'radio',
        'label': '单选框',
        'name': 'radio',
        'value': '',
        'required': false,
        'data': [{
            'key': 'radio1',
            'value': '按钮一',
            'checked': false,
            'disabled': false
        }, {
            'key': 'radio2',
            'value': '按钮二',
            'checked': false,
            'disabled': false
        }]
    }, {
        'active': false,
        'type': 'checkbox',
        'label': '复选框',
        'name': 'checkbox',
        'values': [],
        'required': false,
        'tag': false,
        'data': [{
            'key': 'checkbox1',
            'value': '复选一',
            'checked': false,
            'disabled': false
        }, {
            'key': 'checkbox2',
            'value': '复选二',
            'checked': false,
            'disabled': false
        }]
    }, {
        'active': false,
        'type': 'textarea',
        'label': '文本框',
        'name': 'textarea',
        'value': '',
        'placeholder': '输入文本',
        'rows': 4,
        'required': false
    }, {
        'active': false,
        'type': 'blockquote',
        'text': '引用框',
        'style-nm': false
    }, {
        'active': false,
        'type': 'fieldset',
        'title': '字符集',
        'text': '提示内容'
    }, {
        'active': false,
        'type': 'hr',
        'color': '',
        'color-tips': ['layui-border-red', 'layui-border-orange', 'layui-border-green', 'layui-border-cyan', 'layui-border-blue', 'layui-border-black']
    }];

    /*json对象转html*/
    let jsonObj2html = (o, showRmBtn) => {
        let html = '';
        let delBtn = showRmBtn ? '<i class=" layui-icon-delete  layui-icon"></i>' : '';
        switch (o.type) {
            case 'input':
                html += '<div class="layui-form-item ' + (o.active ? 'layui-form-item-active' : '') + '" dom-type="input">\n' + delBtn +
                    '    <label class="layui-form-label ' + (o.required ? 'required' : '') + '">' + o.label + '</label>\n' +
                    '    <div class="layui-input-block">\n' +
                    '      <input type="' + (o.subtype ? o.subtype : 'text') + '" name="' + o.name + '" ' + (o.required ? 'lay-verify="required"' : '') + ' placeholder="' + o.placeholder + '" class="layui-input" value="' + o.value + '">\n' +
                    '    </div>\n' +
                    '  </div>';
                break;

            case 'select':
                html += '<div class="layui-form-item ' + (o.active ? 'layui-form-item-active' : '') + '" dom-type="select">\n' + delBtn +
                    '       <label class="layui-form-label ' + (o.required ? 'required' : '') + '">' + o.label + '</label>\n' +
                    '       <div class="layui-input-block">\n' +
                    '           <select name="' + o.name + '" lay-filter="' + o.name + '-filter">\n';

                if (o.placeholder && o.placeholder != '' && !o.active)
                    html += '       <option value="">' + o.placeholder + '</option>';

                if (o.data && o.data.length > 0)
                    o.data.forEach(op => {
                        html += '       <option value="' + op.key + '" ' + (op.selected || o.value === op.key ? 'selected' : '') + '>' + op.value + '</option>';
                    });

                html += '       </select>\n' +
                    '       </div>\n' +
                    '    </div>';
                break;

            case 'radio':
                html += '<div class="layui-form-item ' + (o.active ? 'layui-form-item-active' : '') + '" dom-type="radio">\n' + delBtn +
                    '       <label class="layui-form-label ' + (o.required ? 'required' : '') + '">' + o.label + '</label>\n' +
                    '       <div class="layui-input-block">\n';

                if (o.data && o.data.length > 0)
                    o.data.forEach(rd => {
                        html += '<input type="radio" name="' + o.name + '" value="' + rd.key + '" title="' + rd.value + '" ' + (rd.checked || o.value === rd.key ? 'checked' : '') + ' ' + (rd.disabled ? 'disabled' : '') + ' lay-filter="' + o.name + '-filter">';
                    });

                html += '   </div>\n' +
                    '  </div>';
                break;

            case 'checkbox':
                html += '<div class="layui-form-item ' + (o.active ? 'layui-form-item-active' : '') + '" dom-type="checkbox">\n' + delBtn +
                    '       <label class="layui-form-label ' + (o.required ? 'required' : '') + '">' + o.label + '</label>\n' +
                    '       <div class="layui-input-block">\n';

                if (o.data && o.data.length > 0)
                    o.data.forEach((ck, i) => {
                        html += '<input type="checkbox" name="' + o.name + '[' + i + ']" value="' + ck.key + '" ' + (o.tag ? 'lay-skin="tag"' : '') + ' title="' + ck.value + '" ' + (ck.checked || o.values.includes(ck.key) ? 'checked' : '') + ' ' + (ck.disabled ? 'disabled' : '') + '>';
                    });

                html += '   </div>\n' +
                    '  </div>';
                break;

            case 'textarea':
                html += '<div class="layui-form-item ' + (o.active ? 'layui-form-item-active' : '') + '" dom-type="textarea">\n' + delBtn +
                    '    <label class="layui-form-label ' + (o.required ? 'required' : '') + '">' + o.label + '</label>\n' +
                    '    <div class="layui-input-block">\n' +
                    '      <textarea placeholder="' + o.placeholder + '" class="layui-textarea" name="' + o.name + '" rows="' + o.rows + '">' + (o.value ? o.value : '') + '</textarea>\n' +
                    '    </div>\n' +
                    '  </div>';
                break;

            case 'blockquote':
                html += '<div class="layui-form-item ' + (o.active ? 'layui-form-item-active' : '') + '"  dom-type="blockquote">' + delBtn + '<blockquote class="layui-elem-quote ' + (o['style-nm'] ? 'layui-quote-nm' : '') + '">' + o.text + '</blockquote></div>';
                break;

            case 'fieldset':
                html += '<div class="layui-form-item ' + (o.active ? 'layui-form-item-active' : '') + '"  dom-type="fieldset">' + delBtn + '<fieldset class="layui-elem-field"> <legend>' + o.title + '</legend> <div class="layui-field-box">' + o.text + '</div> </fieldset></div>';
                break;

            case 'hr':
                html += '<div class="layui-form-item ' + (o.active ? 'layui-form-item-active' : '') + '"  dom-type="hr">' + delBtn + '<hr class="' + (o.color ?? '') + '"/></div>';
                break;
        }
        return html;
    };

    /*json数组转html*/
    let jsonArr2html = (jsonArr, showRmBtn) => {
        let html = '';
        jsonArr.forEach(o => html += jsonObj2html(o, showRmBtn));
        return html;
    };

    /*绑定删除事件*/
    let bindRmEvent = () => {
        let rmBtns = $('.container-style > .layui-form-item > i');
        if (rmBtns)
            rmBtns.each((i, rmBtn) => {
                $(rmBtn).off('click');
                $(rmBtn).on('click', () => {
                    if ($(rmBtn).parent().hasClass('layui-form-item-active'))
                        $('textarea[name=ele-html-textarea]').val('');
                    $(rmBtn).parent().remove();
                });
            });
    };

    /*绑定选中事件*/
    let bindSelectEvent = () => {
        let items = $('.container-style > .layui-form-item');
        if (items)
            items.each((i, item) => {
                $(item).off('click');
                $(item).on('click', () => $('.container-style > .layui-form-item').each((i, it) => {
                    if (item == it && !$(it).hasClass('layui-form-item-active')) {
                        if (!$(it).hasClass('layui-form-item-active')) {
                            $(it).addClass('layui-form-item-active');

                            $('textarea[name=ele-html-textarea]').val(JSON.stringify(collectDomJson(it), undefined, 2));
                        }
                    } else if (item != it && $(it).hasClass('layui-form-item-active'))
                        $(it).removeClass('layui-form-item-active');
                }));
            });
    };

    /*收集 dom json*/
    let collectDomJson = dom => {
        let json = {};
        switch ($(dom).attr('dom-type')) {
            case 'input':
                let inputLabel = $(dom).children('label');
                let inputInput = $(dom).children('.layui-input-block').children('input');
                json = {
                    'active': $(dom).hasClass('layui-form-item-active'),
                    'type': 'input',
                    'label': inputLabel.text(),
                    'name': inputInput.attr('name'),
                    'value': inputInput.val(),
                    'placeholder': inputInput.attr('placeholder'),
                    'subtype': inputInput.attr('type'),
                    'required': inputLabel.hasClass('required')
                };
                break;

            case 'select':
                let selectLabel = $(dom).children('label');
                let selectSelect = $(dom).children('.layui-input-block').children('select');
                let selectDds = $(dom).children('.layui-input-block').children('.layui-form-select').children('dl').children('dd');
                let selectPlaceholder = '', selectValue = '', selectData = [];

                selectDds.each((i, o) => {
                    if ($(o).hasClass('layui-select-tips'))
                        selectPlaceholder = $(o).text();
                    else
                        selectData.push({
                            'key': $(o).attr('lay-value'),
                            'value': $(o).text(),
                            'selectd': $(o).hasClass('layui-this')
                        });

                    if ($(o).hasClass('layui-this'))
                        selectValue = $(o).attr('lay-value');
                })

                json = {
                    'active': $(dom).hasClass('layui-form-item-active'),
                    'type': 'select',
                    'label': selectLabel.text(),
                    'name': selectSelect.attr('name'),
                    'value': selectValue,
                    'placeholder': selectPlaceholder,
                    'required': selectLabel.hasClass('required'),
                    'data': selectData
                };
                break;

            case 'radio':
                let radioLabel = $(dom).children('label');
                let radioInputs = $(dom).children('.layui-input-block').children('input');
                let radioValue = '', radioData = [];

                radioInputs.each((i, o) => {
                    if (o.checked)
                        radioValue = o.value;

                    radioData.push({
                        'key': o.value,
                        'value': o.title,
                        'checked': o.checked,
                        'disabled': o.disabled
                    });
                });

                json = {
                    'active': $(dom).hasClass('layui-form-item-active'),
                    'type': 'radio',
                    'label': radioLabel.text(),
                    'name': $(radioInputs[0]).attr('name'),
                    'value': radioValue,
                    'required': radioLabel.hasClass('required'),
                    'data': radioData
                };
                break;

            case 'checkbox':
                let checkboxLabel = $(dom).children('label');
                let checkboxInputs = $(dom).children('.layui-input-block').children('input');
                let checkboxValues = [], checkboxData = [];
                checkboxInputs.each((i, o) => {
                    if (o.checked)
                        checkboxValues.push(o.value);

                    checkboxData.push({
                        'key': o.value,
                        'value': o.title,
                        'checked': o.checked,
                        'disabled': o.disabled
                    });
                });
                json = {
                    'active': $(dom).hasClass('layui-form-item-active'),
                    'type': 'checkbox',
                    'label': checkboxLabel.text(),
                    'name': $(checkboxInputs[0]).attr('name').split('[')[0],
                    'values': checkboxValues,
                    'required': checkboxLabel.hasClass('required'),
                    'tag': $(checkboxInputs[0]).attr('lay-skin') ? true : false,
                    'data': checkboxData
                };
                break;

            case 'textarea':
                let textareaLabel = $(dom).children('label');
                let textareaTextarea = $(dom).children('.layui-input-block').children('textarea');
                json = {
                    'active': $(dom).hasClass('layui-form-item-active'),
                    'type': 'textarea',
                    'label': textareaLabel.text(),
                    'name': textareaTextarea.attr('name'),
                    'value': textareaTextarea.val(),
                    'placeholder': textareaTextarea.attr('placeholder'),
                    'rows': textareaTextarea.attr('rows'),
                    'required': textareaLabel.hasClass('required')
                };
                break;

            case 'blockquote':
                json = {
                    'active': $(dom).hasClass('layui-form-item-active'),
                    'type': 'blockquote',
                    'text': $(dom).children('.layui-elem-quote').text(),
                    'style-nm': $(dom).children('.layui-elem-quote').hasClass('layui-quote-nm')
                };
                break;

            case 'fieldset':
                json = {
                    'active': $(dom).hasClass('layui-form-item-active'),
                    'type': 'fieldset',
                    'title': $(dom).children('.layui-elem-field').children('legend').text(),
                    'text': $(dom).children('.layui-elem-field').children('.layui-field-box').text()
                };
                break;

            case 'hr':
                json = {
                    'active': $(dom).hasClass('layui-form-item-active'),
                    'type': 'hr',
                    'color': $(dom).children('hr').attr('class') ?? '',
                    'color-tips': ['layui-border-red', 'layui-border-orange', 'layui-border-green', 'layui-border-cyan', 'layui-border-blue', 'layui-border-black']
                };
                break;
        }

        return json;
    };

    /*导出json数据*/
    let exportJson = () => {
        let doms = $('.container-style').children();
        if (doms && doms.length > 0) {
            let jsonArr = [];
            doms.each((i, o) => jsonArr.push(collectDomJson(o)));
            let blob = new Blob([JSON.stringify(jsonArr, undefined, 2)]);
            let link = document.createElement("a");
            link.href = URL.createObjectURL(blob);
            link.download = 'form-data-' + new Date().getTime() + '.json';
            link.click();
            link.remove();
        } else layer.msg('没有可导出的表单元素');
    };

    /*获取json数组数据*/
    formbuilder.getJsonArr = () => {
        let doms = $('.container-style').children();
        if (doms && doms.length > 0) {
            let jsonArr = [];
            doms.each((i, o) => jsonArr.push(collectDomJson(o)));
            return jsonArr;
        } else layer.msg('没有可导出的表单元素');
    };

    formbuilder.json2html = (id, jsonArr) => {
        let container = $('#' + id);
        container.addClass('layui-form');
        container.html(jsonArr2html(jsonArr, false));
        form.render();
    };

    /*导入json数据*/
    let importJson = e => {
        let file = e.target.files[0];
        if (!file) {
            return false;
        }
        let fileReader = new FileReader();
        fileReader.addEventListener('load', e => {
            try {
                $('.container-style').html(jsonArr2html(JSON.parse(e.target.result), true));
                form.render();
                bindRmEvent();
                bindSelectEvent();
            } catch (err) {
                console.error(err);
                layer.msg('json数据有误');
            }
        });

        fileReader.readAsText(file);
    };

    /*属性配置改变后，重新渲染组件*/
    let renderStyle = () => {
        let active = $('.container-style>.layui-form-item-active');
        if (active && active.length > 0) {
            let json = JSON.parse($('textarea[name=ele-html-textarea]').val());
            active.replaceWith(jsonObj2html(json, true));
            form.render();
            bindRmEvent();
            bindSelectEvent();
        }
    };

    layui.link('layui/module/formbuilder/formbuilder.css');

    formbuilder.render = id => {
        $('#' + id).html('<div class="layui-row">\n' +
            '    <div class="layui-col-xs3">\n' +
            '        <div class="layui-card-body">\n' +
            '            <blockquote class="layui-elem-quote">\n' +
            '                组件库\n' +
            '            </blockquote>\n' +
            '            <div class="container-ele layui-form">\n' +
            '            </div>\n' +
            '        </div>\n' +
            '    </div>\n' +
            '\n' +
            '    <div class="layui-col-xs6">\n' +
            '        <div class="layui-card-body">\n' +
            '            <blockquote class="layui-elem-quote">\n' +
            '                表单样式\n' +
            '            </blockquote>\n' +
            '            <div class="container-style layui-form">\n' +
            '            </div>\n' +
            '        </div>\n' +
            '    </div>\n' +
            '\n' +
            '    <div class="layui-col-xs3">\n' +
            '        <div class="layui-card-body">\n' +
            '            <blockquote class="layui-elem-quote">\n' +
            '                组件属性\n' +
            '            </blockquote>\n' +
            '            <div class="container-props layui-form">\n' +
            '               <textarea name="ele-html-textarea" placeholder="组件html代码...." rows="20" class="layui-textarea"></textarea>' +
            '            </div>\n' +
            '            <div><button type="button" id="update-ele-button" class="layui-btn layui-btn-fluid"><i class="layui-icon-release layui-icon"></i>  渲染表单</button></div>' +
            '            <hr>' +
            '            <div><button type="button" id="export-json" class="layui-btn layui-btn-fluid"><i class="layui-icon-export layui-icon"></i>  导出 json 数据</button></div>' +
            '            <div><label for="import-json" class="layui-btn layui-btn-fluid"><input type="file" id="import-json" accept="application/json"/><i class="layui-icon-upload layui-icon"></i>  导入 json 数据</label></div>' +
            '        </div>\n' +
            '    </div>\n' +
            '</div>');

        $('.container-ele').html(jsonArr2html(templateJsonArr, true));

        form.render();

        sortable.render($('.container-ele')[0], {
            group: {
                name: 'fm',
                pull: 'clone',
                put: false
            },
            animation: 150,
            sort: false,
            onEnd: event => {
                form.render();
                bindRmEvent();
                bindSelectEvent();
            }
        });
        sortable.render($('.container-style')[0], {
            group: 'fm',
            animation: 150
        });

        $('#update-ele-button').click(() => renderStyle());
        $('textarea[name=ele-html-textarea]').blur(() => renderStyle());

        $('#export-json').click(() => exportJson());

        document.getElementById('import-json').addEventListener('change', importJson, false);
        return formbuilder;
    };

    exports('formbuilder', formbuilder);
});